今天討論的東西會也很多(每天啃到天荒地老)
Reason - Record
很像 Javascript 的 Object
但是他有幾個特性
type person = {
age: int,
name: string
};
let tomas = {
age: 35,
name: "Tomas Lin"
};
let tomasName = tomas.name;
print_endline(tomasName); /* Tomas Lin */
Record 需要先宣告一個基本的結構 (Type)
否則會報錯
如果符合則會自動型別推導
type person = {
age: int,
name: string
};
let tomas: person = {
age: 35,
name: "Tomas Lin"
};
let tomasName = tomas.name;
print_endline(tomasName);
let simon = {
age: 22,
name: "Simon"
};
上面的範例中 tomas
有指定型別是 person
但是 simon
並未指定,可是因為他的結構和 person
一樣
所以 Reason 會自動做型別推導為 person
note:若是有兩個同樣型別結構的話則會依據較近的型別
你也可以將型別宣告在別的檔案中
但是需要明確的指定名稱
AnimalType.re
type cat = {color: string, call: string};
Animal.re
let whiteCat: AnimalType.cat = {
color: "white",
call: "喵!"
};
print_endline(whiteCat.call);
因為 Record
是不可以直接修改的
可以利用 Spread
來做修改的實作
範例如下
type person = {
age: int,
name: string
};
let tomas: person = {
age: 35,
name: "Tomas Lin"
};
let tomasName = tomas.name;
print_endline(tomasName);
let simon = {
age: 22,
name: "Simon"
};
let tomasNextYear = {...tomas, age: tomas.age + 1};
Js.log(tomasNextYear.age);/* 36 */
注意:Spread修改不能夠增加新的欄位
Reason
也保有直接修改的彈性
範例如下:
type person = {
age: int,
mutable name: string
};
let tomas: person = {
age: 25,
name: "Tomas Lin"
};
let name = "Simon";
let simon = {
age: 25,
name
};
tomas.age = tomas.age + 1;
Js.log(tomas.age); /* 26 */
Js.log(simon.name); /* Simon */
也支援 punning
簡化程式碼
Reason
中你無法宣告一個 function
他的參數傳入一個物件
這個物件中必要參數是 age
而其他參數都是可以變動的
範例如下
type person = {age: int, name: string};
type monster = {age: int, hasTentacles: bool};
let getAge = (entity) => entity.age;
let kraken = {age: 9999, hasTentacles: true};
let me = {age: 5, name: "Baby Reason"};
getAge(kraken);
getAge(me);
/* get Error:
This has type:
person
But somewhere wanted:
monster */
Type system 不能定義一個類型是 person
或 monster
當你有這類型需求的話
接下來介紹的 Reason - Object
可以滿足
有些時候 Reason - Record
無法滿足一些特殊情境的需求
所以便有了 Reason - Object
增加一些彈性
物件不一定要宣告型別
看起來和 Reason - Record
十分相似
除了 .
和 ..
範例如下
type tesla = {
.
color: string
};
開頭 .
代表這是一個閉鎖的物件
任何基於這個型別的物件都必須完全符合結構
type car(`a) = {
..
color: string
} as `a;
開頭 ..
則代表這是開放型的物件,所以可以包含其他值或 function
開放型的物件代表是多型
所以需要一個參數
type tesla = {
.
color: string,
};
let obj: tesla = {
val red = "Red";
pub color = red;
};
Js.log(obj#color) /* "Red" */
在這個物件中含有兩個屬性
私有屬性 red
和 color
method
因為 color
是一個公開的 method
所以我們可以利用 object notation
來標記取得值
物件只導出Method,所有屬性都是Private的
type tesla = {.
drive: (int) => int
};
let obj: tesla = {
val hasEnvy = ref(false);
pub drive = (speed) => {
this#enableEnvy(true);
speed;
};
pri enableEnvy = (envy) => hasEnvy := envy
};
在這個範例中 tesla
中一個公開的 drive
也有一個私有的 enableEnvy
method
私有的 method 只可以在物件內部取用
Reason 也有 this
和 Javascript 的 this
不一樣的地方在於
Reason 的 this
永遠都會指向物件本身
type tesla('a) = {
..
drive: int => int
} as 'a;
type teslaParams = {.
drive: int => int,
doYouWant: unit => bool
};
let obj: tesla(teslaParams) = {
val hasEnvy = ref (false);
pub drive = (speed) => {
this#enableEnvy(true);
speed;
};
pub doYouWant = () => hasEnvy^;
pri enableEnvy = (envy) => hasEnvy := envy
};
Js.log(obj#doYouWant());
Js.log(obj#drive(11));
Js.log(obj#doYouWant());
上面的 tesla
中為開放型物件
但是需要有一個參數 teslaParams
定義所有開放的的 method
但是如果你要找由 Javascript
的物件,你需要的不是 Reason - Object
而是特別的 BukleScript - Record
special Record
解構是很清楚簡單的方式來取得物件或陣列中的變數
下面綁定 ten = 10
和 twenty = 20
let someInts = (10, 20);
let (ten, twenty) = someInts;
Js.log(ten);
Js.log(twenty);
下列綁定變數 name = "Guy"
和 age = 30
type person = {name: string, age: int};
let somePerson: person = {name: "Tomas", age: 30};
let {name, age} = somePerson;
Js.log(name);
Js.log(age);
取回變數後可以重新命名
type person = {name: string, age: int};
let somePerson: person = {name: "Tomas", age: 30};
let {name: n, age: a} = somePerson;
Js.log(n);
Js.log(a);
也可以定義型態
type person = {name: string, age: int};
let somePerson: person = {name: "Tomas", age: 30};
let {name: (n: string), age: (a: int)} = somePerson;
Js.log(n);
Js.log(a);
當然函式的標籤式參數也可以解構
type person = {
name: string,
age: int
};
let tomas = {name: "Tomas", age: 25};
let someFunction = (~person as {name}) => {
Js.log(name);
};
let otherFunction = (~person as {name} as thePerson) => {
Js.log(thePerson);
Js.log(name);
};
someFunction(~person=tomas);
otherFunction(~person=tomas);
但是 BuckleScript 怎麼處理 Object 呢?
這就是明天的故事了